package databook;

import databook.util.MyColorAction;
import databook.util.MySizeAction;
import databook.util.SimpleTransformFunction;
import databook.util.CSVConvert;
import databook.util.CSVReader;
import databook.util.SquareGridLayout;
import databook.util.MajorMap;
import databook.controller.Props;
import databook.controller.PropertyListener;
import databook.controller.PropertyEvent;

import java.util.Iterator;
import java.util.HashMap;
import java.util.Collection;
import java.util.Arrays;
import javax.swing.JPanel;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.NumberFormat;
//import java.awt.event.ComponentAdapter;
//import java.awt.event.ComponentEvent;
//import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.ItemAction;
import prefuse.action.assignment.SizeAction;
import prefuse.action.animate.SizeAnimator;
import prefuse.action.animate.ColorAnimator;
import prefuse.action.animate.LocationAnimator;
import prefuse.action.assignment.ColorAction;
import prefuse.action.layout.RandomLayout;
import prefuse.action.layout.graph.*;
//import prefuse.action.layout.graph.RadialTreeLayout;
import prefuse.controls.DragControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Edge;
import prefuse.data.expression.Predicate;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.visual.VisualItem;
import prefuse.visual.tuple.TableNodeItem;
import prefuse.visual.tuple.TableEdgeItem;
import prefuse.data.io.DataIOException;
import prefuse.visual.sort.ItemSorter;
import prefuse.controls.Control;
import prefuse.controls.ControlAdapter;
//import prefuse.controls.ToolTipControl;
//import prefuse.controls.ZoomToFitControl;

/**
 * Based on http://prefuse.org/doc/manual/introduction/example/Example.java
 * 
 * @author smstang
 */

public class DatabookViz extends JPanel {

    /* File names */
    public final static String COHORT_SRC = "cohort.csv";
    public final String NODE_FILE_PREFIX = "node";
    public final String EDGE_FILE_PREFIX = "edge";
    public final String TREE_SUFFIX = "_tree.csv";
    
    /* Data Fields */
    //public String EDGE_WEIGHT = "edge_weight";
    public String MAJOR = "major";
    public String COLLEGE = "college";
    public String DEPT = "department";
    
    public static String TREE_COLUMN = "Tree";
    public static String SIZE_STRING = "Size_String";
    public static String SELECTED_COL = "SELECTED";
    
    public Visualization viz;
    //private MajorGraph majorGraph;  // Made this a property of the class because I need to refer to it later
    //public ForceSimulator fsim;
    
    public WeightTree nodeTree;
    private WeightTree edgeTree;

    private Predicate[] visible;
    private Predicate[] hidden;
    
    private Graph[] graphs;
    //private String[] gnames = new String[] {"COLLEGE","DEPT","MAJOR"};
    private String[] gnames = new String[] {"MAJOR"};
    private int selectedGraph = 0;
    
    private SimpleTransformFunction[][] transformFuncs;
    
    private Control interControl;
    
    private static NumberFormat percentFormatter = NumberFormat.getPercentInstance();
    private static NumberFormat intFormatter = NumberFormat.getIntegerInstance();
	
    /**
     * constructor
     */
    public DatabookViz() {
        this(COHORT_SRC);
    }
    
    public DatabookViz(String srcFile)
    {
        viz = new Visualization();
        initGraphs(srcFile);
        this.add(createDisplay());
        setPropertyListeners();
    }
     
    protected void initGraphs(String file) {
        // Parse/Convert the data.  Will hopefully only need to happen once.
        try {
            // This will take data from cohort.csv and create 4 files.
            // edge.csv which will be usable for an edge table
            // edge_tree.csv which will hold the tree structure with the weights
            // node.csv which will be for the node table
            // node_tree.csv which will hold the tree structure for the node stats.
            System.out.println("Parsing: " + file);
            CSVConvert.convert2CSV(file, EDGE_FILE_PREFIX, NODE_FILE_PREFIX,TREE_SUFFIX,false);
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        
        // Creates a filter for filtering.
        HashMap<String,String> filterMap = new HashMap();
        filterMap.put("MAJOR_FROM","MAJOR");
        filterMap.put("MAJOR_TO","MAJOR");
        filterMap.put("TERM_FROM","TERM");
        filterMap.put("TERM_TO","TERM");
        filterMap.put("COLLEGE_FROM","COLLEGE");
        filterMap.put("COLLEGE_TO","COLLEGE");
        filterMap.put("DEPT_FROM","DEPT");
        filterMap.put("DEPT_TO","DEPT");
        try {
            nodeTree = new WeightTree(new CSVReader(NODE_FILE_PREFIX + TREE_SUFFIX).parseTable());
            edgeTree = new WeightTree(new CSVReader(EDGE_FILE_PREFIX + TREE_SUFFIX).parseTable(),filterMap);
            //System.out.println(nodeTree.printTree(true));
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        } catch (DataIOException dioe) {
            dioe.printStackTrace();
        }
        
        try {
            String[] values = (String[])nodeTree.getValues("TERM").toArray(new String[0]);
            Arrays.sort(values);
            Props.set(Props.NUMBER_OF_TERMS,values.length);
            Props.set(Props.START_TERM,values[0]);
            Props.set(Props.START_YEAR,Integer.parseInt(values[0].substring(0,4)));
            Props.set(Props.END_TERM,values[values.length-1]);
            Props.set(Props.END_YEAR,Integer.parseInt(values[values.length-1].substring(0,4)));
            //Props.setDefault(Props.CURRENT_RANGE_MIN,Props.getInt(Props.START_YEAR));
            //Props.setDefault(Props.CURRENT_RANGE_MAX,Props.getInt(Props.END_YEAR));
        } catch (Exception e) {
            System.err.println("Bad Integer Parse: " + e);
        }
        
        HashMap<String,Node> nodes = new HashMap<String,Node>();
        graphs = new Graph[gnames.length];
        transformFuncs = new SimpleTransformFunction[gnames.length][];
        visible = new Predicate[gnames.length];
        hidden = new Predicate[gnames.length];
        
        for (int i = 0; i < gnames.length; i++) {
            Graph g = new Graph(true);
            g.addColumn("ID",String.class);
            g.getNodeTable().addColumn(TREE_COLUMN,WeightTree.class);
            g.addColumn(SELECTED_COL,String.class);
            g.addColumn(SIZE_STRING,String.class);
            graphs[i] = g;
            Collection treeNodes = nodeTree.getNodes(gnames[i]);
            Iterator iter = treeNodes.iterator();
            while (iter.hasNext()) {
                Node n = g.addNode();
                WeightTree wt = (WeightTree)iter.next();
                n.setString("ID",nodify(wt.getValue()));
                n.set(TREE_COLUMN,wt);
                nodes.put(gnames + wt.getValue(),n);
            }
            g.getEdgeTable().addColumn(TREE_COLUMN,WeightTree.class);
            
            Collection links = edgeTree.getLinks(gnames[i] + "_FROM",gnames[i] + "_TO");
            iter = links.iterator();
            while (iter.hasNext()) {
                WeightTree[] link = (WeightTree[])iter.next();
                if (!link[0].getValue().equals(link[1].getValue())) {
                    link[1].setNodes((WeightTree)nodes.get(gnames + link[0].getValue()).get(TREE_COLUMN), 
                                    (WeightTree)nodes.get(gnames + link[1].getValue()).get(TREE_COLUMN));
                    Edge e = g.addEdge(nodes.get(gnames + link[0].getValue()),nodes.get(gnames + link[1].getValue()));
                    e.set(TREE_COLUMN,link[1]);
                }
            }
            
            transformFuncs[i] = new SimpleTransformFunction[] 
                                                {new SimpleTransformFunction(),
                                                 new SimpleTransformFunction()};
        }

        setTransform();
    }
    
    public String[] getDateRange() {
        String[] values = (String[])nodeTree.getValues("TERM").toArray();
        Arrays.sort(values);
        return new String[] {values[0],values[values.length]};
    }
    
    public String nodify(String name) {
        String[] split = name.split(" ");
        int ind = 0;
        int max = 4;
        String ret = "";
        for (int i = 0; i < split.length; i++) {
            ind += split[i].length();
            if (ind > max) {
                ret += split[i] + "\n";
                ind = 0;
            } else {
                ret += split[i] + " ";
            }
        }
        return ret.substring(0,ret.length()-1);
    }
    
    public void setTransform() {
        boolean normalize = Props.getBoolean(Props.NORMALIZE_SIZE);
        for (int i = 0; i < gnames.length; i++) {
            transformFuncs[i][0].setLowValue(nodeTree.getMinSize(gnames[i],!normalize));
            transformFuncs[i][0].setHighValue(nodeTree.getMaxSize(gnames[i],!normalize));
            transformFuncs[i][1].setLowValue(edgeTree.getMinSize(gnames[i] + "_TO",!normalize,gnames[i] + "_TO",true));
            transformFuncs[i][1].setHighValue(edgeTree.getMaxSize(gnames[i] + "_TO",!normalize,gnames[i] + "_TO",true));
            //System.out.println("Node Root: " + nodeTree.getWeight());
            //System.out.println("Node Low to High: " + transformFuncs[i][0].getLowValue() + " " + transformFuncs[i][0].getHighValue());
            //System.out.println("Edge Low to High: " + transformFuncs[i][1].getLowValue() + " " + transformFuncs[i][1].getHighValue());
            if (i == getSelectedGraph()) {
                if (Props.getBoolean(Props.PERCENT_IN) || Props.getBoolean(Props.PERCENT_OUT)) {
                    Props.set(Props.MAX_THRESHHOLD,(int)transformFuncs[i][1].getHighValue()*100);
                } else {
                    Props.set(Props.MAX_THRESHHOLD,(int)transformFuncs[i][1].getHighValue());
                }
            }
        }
    }
    
    /**
     * Puts the graph together
     * 
     * @param 
     * @return
     */
    private Display createDisplay() 
    {
        // draw the "name" label for NodeItems
        LabelRenderer r = new LabelRenderer("ID");
        r.setRoundedCorner(8, 8); // round the corners
        
        // create a new default renderer factory
        // edges are curved and have arrows
        DefaultRendererFactory rf = new DefaultRendererFactory(r);
        rf.setDefaultEdgeRenderer(new FlowEdgeRenderer());
        
        viz.setRendererFactory(rf);
        
                
        //display and controls
        Display d = new Display(viz);
        d.setSize(700, 600); // set display size
        // drag individual items around
        d.addControlListener(new DragControl());
        // pan with left-click drag on background
        d.addControlListener(new PanControl()); 
        // zoom with right-click drag
        d.addControlListener(new ZoomControl());
        
        d.setItemSorter(new ItemSorter() {
            public int score(VisualItem item) {
                int score = super.score(item);
                score += ((WeightTree)item.get(TREE_COLUMN)).getWeight();
                return score;
            }
        });
        
        interControl = new ControlAdapter() {
            private void unselect(VisualItem item) {
                item.setStrokeColor(Props.getColor(Props.UNSELECTED_NODE_BORDER));
                unhover((VisualItem)Props.get(Props.SELECTED_ITEM));
                Props.set(Props.SELECTED_ITEM,null);
                viz.run(gnames[getSelectedGraph()] + "_color");
            }
            
            private void select(VisualItem item) {
                item.setStrokeColor(Props.getColor(Props.SELECTED_NODE_BORDER));
                hover(item);
                
                Props.set(Props.SELECTED_ITEM,item);
                viz.run(gnames[getSelectedGraph()] + "_color");
            }
            
            public void hover(VisualItem item) {
                item.setFillColor(Props.getColor(Props.SELECTED_NODE));
                item.setString(SELECTED_COL,Props.SELECTED_NODE);
                
                Node sel = (Node)item;
                Graph g = sel.getGraph();
                Iterator neighbors = ((Node)item).neighbors();
                while (neighbors.hasNext()) {
                    Node neighbor = (Node)neighbors.next();                    
                    Edge to = g.getEdge(sel,neighbor);
                    Edge from = g.getEdge(neighbor,sel);
                    boolean source = false;
                    boolean target = false;
                    if (Props.getBoolean(Props.HIGHLIGHT_OUT))
                    {
                    	if (to != null && ((VisualItem)to).isVisible()) {
                            source = true;
                            ((VisualItem)to).setStrokeColor(Props.getColor(Props.SELECTED_OUT_EDGE));
                            ((VisualItem)to).setFillColor(Props.getColor(Props.SELECTED_OUT_EDGE));
                            to.setString(SELECTED_COL,Props.SELECTED_OUT_EDGE);
                            ((VisualItem)neighbor).setFillColor(Props.getColor(Props.SELECTED_SOURCE_NODE));
                            neighbor.setString(SELECTED_COL,Props.SELECTED_SOURCE_NODE);
                    	}
                    }
                    if (Props.getBoolean(Props.HIGHLIGHT_IN))
                    {
                    	if (from != null && ((VisualItem)from).isVisible()) {
                            target = true;
                            ((VisualItem)from).setStrokeColor(Props.getColor(Props.SELECTED_IN_EDGE));
                            ((VisualItem)from).setFillColor(Props.getColor(Props.SELECTED_IN_EDGE));
                            from.setString(SELECTED_COL,Props.SELECTED_IN_EDGE);
                            ((VisualItem)neighbor).setFillColor(Props.getColor(Props.SELECTED_TARGET_NODE));
                            neighbor.setString(SELECTED_COL,Props.SELECTED_TARGET_NODE);
                    	}
                    }
                    if (source && target) {
                        ((VisualItem)neighbor).setFillColor(Props.getColor(Props.SELECTED_T_S_NODE));
                        neighbor.setString(SELECTED_COL,Props.SELECTED_T_S_NODE);
                    }
                }
            }
            
            private void unhover(VisualItem item) {
                item.setFillColor(item.getEndFillColor());
                item.setString(SELECTED_COL,null);
                Node sel = (Node)item;
                Graph g = sel.getGraph();
                Iterator neighbors = ((Node)item).neighbors();
                while(neighbors.hasNext()) {
                    Node neighbor = (Node)neighbors.next();
                    neighbor.setString(SELECTED_COL,null);
                    ((VisualItem)neighbor).setFillColor(Props.getColor(Props.UNSELECTED_NODE));
                    VisualItem to = (VisualItem)g.getEdge(sel,neighbor);
                    VisualItem from = (VisualItem)g.getEdge(neighbor,sel);
                    if (to != null) {
                        to.setStrokeColor(to.getEndStrokeColor());
                        to.setFillColor(to.getEndFillColor());
                        to.setString(SELECTED_COL,null);
                    }
                    if (from != null) {
                        from.setStrokeColor(from.getEndStrokeColor());
                        from.setFillColor(from.getEndFillColor());
                        from.setString(SELECTED_COL,null);
                    }
                }
            }
            
            /**
             * When the mouse enters an items area.  Can be used for hovering
             */
            public void itemEntered(VisualItem item, MouseEvent evt) {
                ((Display)evt.getSource()).setToolTipText(getLabel(item));
                if (item.isInGroup(gnames[getSelectedGraph()] + ".nodes")) {
                    if ((Props.get(Props.SELECTED_ITEM) == null) && (Props.getBoolean(Props.HIGHLIGHT_IN) || Props.getBoolean(Props.HIGHLIGHT_OUT))) {
                        hover(item);
                        viz.repaint();
                    }
                }
            }
            
            /**
             * When the mouse enters an items area.  Can be used for hovering
             */
            public void itemExited(VisualItem item, MouseEvent evt) {
                ((Display)evt.getSource()).setToolTipText(null);
                if (item.isInGroup(gnames[getSelectedGraph()] + ".nodes")) {
                    if ((Props.get(Props.SELECTED_ITEM) == null) && (Props.getBoolean(Props.HIGHLIGHT_IN) || Props.getBoolean(Props.HIGHLIGHT_OUT))) {
                        unhover(item);
                        viz.repaint();
                    }
                }
            }
            
            /**
             * When an item is clicked.  Can be used for selecting
             */
            public void itemClicked(VisualItem item, MouseEvent e)
            { 
                if( e.getButton() == MouseEvent.BUTTON1) {
                    if (Props.get(Props.SELECTED_ITEM) != null) {
                        unselect((VisualItem)Props.get(Props.SELECTED_ITEM));
                    }
                    if (item.isInGroup(gnames[getSelectedGraph()] + ".nodes")) {
                        select(item);
                    } else {
                        Props.set(Props.CLICKED_EDGE,item);
                    }
                    viz.repaint();
                }
            }
	
            /**
             * gets called when a mouse click occurs not on a visual item
             */
            public void mouseClicked(MouseEvent e) 
            {
		if(e.getButton() == e.BUTTON1) {
                    if (Props.get(Props.SELECTED_ITEM) != null) {
                        unselect((VisualItem)Props.get(Props.SELECTED_ITEM));
                        viz.repaint();
                    }
                }
            } 
        };

        d.addControlListener(interControl);
        
        for (int i = 0; i < gnames.length; i++) {
            viz.addGraph(gnames[i],graphs[i]);
            
            transformFuncs[i][0].setStartValue(.75);
            transformFuncs[i][0].setEndValue(2.75);
            transformFuncs[i][1].setStartValue(.25);
            transformFuncs[i][1].setEndValue(10);
            transformFuncs[i][1].setStartColor(Props.getColor(Props.DEFAULT_START_EDGE));
            transformFuncs[i][1].setEndColor(Props.getColor(Props.DEFAULT_END_EDGE));
            
            //Fill Color
            ColorAction fill = new MyColorAction(gnames[i] + ".nodes",
                        VisualItem.FILLCOLOR, Props.getColor(Props.DEFAULT_NODE),this);
            
            //Text Color
            // use black for node text
            ColorAction text = new MyColorAction(gnames[i] + ".nodes",
                        VisualItem.TEXTCOLOR, Props.getColor(Props.DEFAULT_NODE_TEXT),this);
            
            ColorAction edges = new MyColorAction(gnames[i] + ".edges",
                        VisualItem.STROKECOLOR,transformFuncs[i][1],this);
            
            ColorAction arrows = new MyColorAction(gnames[i] + ".edges",
                        VisualItem.FILLCOLOR,transformFuncs[i][1],this);
            
            ItemAction isVisible = new ItemAction(gnames[i] + ".edges",null) {
                public void process(VisualItem item, double frac) {
                    TableEdgeItem edge = (TableEdgeItem)item;
                    
                    //int weight = ((WeightTree)edge.get(TREE_COLUMN)).getWeight();
                    if (((WeightTree)edge.get(TREE_COLUMN)).getSize() * (Props.usePercent() ? 100 : 1) > Props.getInt(Props.EDGE_THRESHHOLD,0) &&
                        ((WeightTree)edge.getSourceItem().get(TREE_COLUMN)).getSize() > 0 &&
                        ((WeightTree)edge.getTargetItem().get(TREE_COLUMN)).getSize() > 0) {
                        //System.out.println("Processing " + ((WeightTree)edge.get(TREE_COLUMN)).getWeight() + " " + edge.getSourceItem().get("ID") + " " + ((WeightTree)edge.getSourceItem().get(TREE_COLUMN)).getWeight() + " " + edge.getTargetItem().get("ID") + " " + ((WeightTree)edge.getTargetItem().get(TREE_COLUMN)).getWeight() );
                        item.setVisible(true);
                    } else {
                        item.setVisible(false);
                    }
                }
            };
            SizeAction edge_size = new MySizeAction(gnames[i] + ".edges",transformFuncs[i][1]);
            SizeAction node_size = new MySizeAction(gnames[i] + ".nodes",transformFuncs[i][0]);
            ItemAction hideUnused = new ItemAction(gnames[i] + ".nodes",null) {
                public void process(VisualItem item, double frac) {
                    //System.out.println("Hiding");
                    TableNodeItem node = (TableNodeItem)item;
                    Iterator iter = node.inEdges();
                    while (iter.hasNext()) {
                        if (((VisualItem)iter.next()).isVisible()) {
                            item.setVisible(true);
                            return;
                        }
                    }
                    iter = node.outEdges();
                    while (iter.hasNext()) {
                        if (((VisualItem)iter.next()).isVisible()) {
                            item.setVisible(true);
                            return;
                        }
                    }
                    item.setVisible(false);
                }
            }; 

            // create an action list containing all color assignments
            ActionList color = new ActionList();
            color.add(fill);
            color.add(text);
            color.add(edges);
            color.add(arrows);
            //color.add(new RepaintAction());
            
            // This resizes the edges and nodes.  No need to put this with the color action list
            // especially if we are going to run it all the time.  
            // isVisible checks to make sure that any hidden items shouldn't be displayed
            // hideUnused checks for any nodes that don't have any edges going in or out that
            // are greater than 0.
            ActionList resize = new ActionList();
            resize.add(isVisible);
            resize.add(hideUnused);
            resize.add(edge_size);
            resize.add(node_size);
            //resize.add(new RepaintAction());
            
            // Animate the resizing and color.  Only use if color
            // is run every so often.  If it is run continously, then
            // this automation will look bad.
            ActionList slowAnimate = new ActionList(1000);
            slowAnimate.add(new SizeAnimator(gnames[i] + ".nodes"));
            slowAnimate.add(new SizeAnimator(gnames[i] + ".edges"));
            slowAnimate.add(new ColorAnimator(gnames[i] + ".edges"));
            slowAnimate.add(new LocationAnimator(gnames[i] + ".nodes"));
            slowAnimate.add(new RepaintAction());
            
            ActionList fastAnimate = new ActionList();
            //fastAnimate.add(new SizeAnimator(gnames[i] + ".nodes"));
            //fastAnimate.add(new SizeAnimator(gnames[i] + ".edges"));
            //fastAnimate.add(new ColorAnimator(gnames[i] + ".edges"));
            fastAnimate.add(new RepaintAction());
            
            //layout
            ActionList layout = new ActionList();
            layout.add(new SquareGridLayout(gnames[i] + ".nodes"));

            // add the actions to the visualization
            viz.putAction(gnames[i] + "_layout", layout);
            viz.putAction(gnames[i] + "_resize", resize);
            viz.putAction(gnames[i] + "_color", color);
            viz.putAction(gnames[i] + "_animate",slowAnimate);
            viz.putAction(gnames[i] + "_fanimate",fastAnimate);
            viz.alwaysRunAfter(gnames[i] + "_resize",gnames[i] + "_color");
            
            // start up the animated layout
            viz.run(gnames[i] + "_resize");
            viz.run(gnames[i] + "_layout");
            
            
        }

        return d;
    }   
    
    public void colorNeighbors()
    {
    	//if nothing is selected then we don't have to do anything (it will draw correctly upon selection)
        //if something is selected then we want to correctly label the items as either selected or not
        //the new method passing information to the ColorAction is to put the field that the node resembles
        //in its selection field instead of just a boolean.  This allows us not to rely on the start and end
        //being what they were before, as if something is changed, and the color action is called again,
        //then the colors will not be remedied by setting the start or end color.  So just set the select
        //col and re-run color animator and repaint.
    	if(Props.get(Props.SELECTED_ITEM) != null)
    	{
            VisualItem item = (VisualItem)Props.get(Props.SELECTED_ITEM);
            Node sel = (Node)item;
            Graph g = sel.getGraph();
            Iterator neighbors = ((Node)item).neighbors();
            while (neighbors.hasNext()) {
                Node neighbor = (Node)neighbors.next();                    
                Edge to = g.getEdge(sel,neighbor);
                Edge from = g.getEdge(neighbor,sel);
                boolean source = false;
                boolean target = false;
                if (Props.getBoolean(Props.HIGHLIGHT_OUT))
                {
                    if (to != null && ((VisualItem)to).isVisible()) {
                        source = true;
                        to.setString(SELECTED_COL,Props.SELECTED_OUT_EDGE);
                        neighbor.setString(SELECTED_COL,Props.SELECTED_TARGET_NODE);
                    }
                }
                if (Props.getBoolean(Props.HIGHLIGHT_IN))
                {
                    if (from != null && ((VisualItem)from).isVisible()) {
                        target = true;
                        from.setString(SELECTED_COL,Props.SELECTED_IN_EDGE);
                        neighbor.setString(SELECTED_COL,Props.SELECTED_SOURCE_NODE);
                    }
                }
                if(!Props.getBoolean(Props.HIGHLIGHT_OUT))
                {
                    VisualItem visItemTo = (VisualItem)to;
                     if (to != null) {
                         visItemTo.set(SELECTED_COL,null);
                     }
                }
                if(!Props.getBoolean(Props.HIGHLIGHT_IN))
                {
                    VisualItem visItemFrom = (VisualItem)from;

                    if (from != null) {
                         visItemFrom.set(SELECTED_COL,null);
                    }
                }

                if (source && target) {
                    neighbor.setString(SELECTED_COL,Props.SELECTED_T_S_NODE);
                }
                if (!source && !target) {
                    neighbor.set(SELECTED_COL,null);
                }
            }
            viz.run(gnames[getSelectedGraph()] + "_color");
            viz.repaint();
    	}
    	
    }
    
    public int getSelectedGraph() {
        return selectedGraph;
    }
    
    public void setPropertyListeners() {
        Props.addListener(new String[] {Props.NORMALIZE_SIZE,Props.EDGE_THRESHHOLD,Props.PERCENT_IN},new PropertyListener() {
            public void propertyChanged(PropertyEvent evt) {
                setTransform();
                redraw(false);
            }
        });
        
        Props.addListener(new String[] {Props.HIGHLIGHT_IN,Props.HIGHLIGHT_OUT},new PropertyListener() {
            public void propertyChanged(PropertyEvent evt) {
                colorNeighbors();
                //redraw();
            }
        });
        
        Props.addListener(Props.CURRENT_RANGE_MIN,new PropertyListener() {
            public void propertyChanged(PropertyEvent evt) {
                System.out.println("Number of Times Run");
                nodeTree.resetChildShowCache();
                edgeTree.resetChildShowCache();
                redraw();
            }
        });
        
        Props.addFilterListener(new PropertyListener() {
            public void propertyChanged(PropertyEvent evt) {
                redraw();
                
            }
        });  
    }
    
    public void redraw() {
        redraw(true);
    }
    
    public void redraw(boolean transform) {
        if (transform && Props.getBoolean(Props.NORMALIZE_SIZE))
            setTransform();
        viz.run(gnames[getSelectedGraph()] + "_resize");
        if (Props.getBoolean(Props.AUTO_LAYOUT)) {
            viz.run(gnames[getSelectedGraph()] + "_layout");
            viz.run(gnames[getSelectedGraph()] + "_animate");
        } else {
            viz.run(gnames[getSelectedGraph()] + "_fanimate");
            //viz.repaint();
        }
    }
    
    public static String getEdgeLabel(Edge e) {
        return e.getGraph().getSourceNode(e).get("ID") + " to " + e.getGraph().getTargetNode(e).get("ID") + ": " + DatabookViz.getEdgeSizeString(e);
    }
    
    public static String getEdgeSizeString(Edge e) {
        double size = getEdgeSize(e);
        if (Props.usePercent()) {
            return percentFormatter.format(size) + " (" + ((WeightTree)e.get(DatabookViz.TREE_COLUMN)).getWeight() + ")";
        } else {
            return intFormatter.format(size);
        }
    }
    
    public static double getEdgeSize(Edge e) {
        return ((WeightTree)e.get(DatabookViz.TREE_COLUMN)).getSize();
    }
    
    public static String getNodeLabel(Node node) {
        String name = node.getString("ID");
        if (MajorMap.isMajor(name)) {
            name += " - " + MajorMap.getMajorName(name);
        } else if (MajorMap.isCollege(name)) {
            name += " - " + MajorMap.getCollegeName(name);
        } 
        name += ": ";
        if (Props.getBoolean(Props.PERCENT_IN,false) || Props.getBoolean(Props.PERCENT_OUT)) {
            name += percentFormatter.format(((WeightTree)node.get(TREE_COLUMN)).getSize());
            name += " (" + ((WeightTree)node.get(TREE_COLUMN)).getWeight() + ")";
        } else {
            name += intFormatter.format(((WeightTree)node.get(TREE_COLUMN)).getSize());
        }
        return name;
    }
    
    public static String getLabel(VisualItem item) {
        if (item instanceof Edge)
            return getEdgeLabel((Edge)item);
        else if (item instanceof Node)
            return getNodeLabel((Node)item);
        else
            return "Unknown";
    }
}
